package root.message;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import root.historic.event.EmprestimoEvent;
import root.historic.event.InteressePeloItemEvent;
import root.historic.event.ItemEvent;
import root.item.ItemOferecidoEvent;
import root.item.PublicarPedidoEvent;
import root.user.User;
import root.user.UserManager;
import strategy.GerenciadorMensagemIF;
import exceptions.BorrowDoesNotExist;
import exceptions.InexistentTopic;
import exceptions.InexistentType;
import exceptions.InvalidMessage;
import exceptions.InvalidReceiver;
import exceptions.InvalidSender;
import exceptions.InvalidSubject;
import exceptions.InvalidTopic;
import exceptions.InvalidType;

/**
 * Classe gerenciadora de mensagens.
 * Encarregada das operacoes que envolvem
 * mensagens. Utiliza o padrao Strategy.
 */
public class GerenciadorMensagem implements GerenciadorMensagemIF{

	private List<MessageTopic> topics;
	private User user;
	
	public GerenciadorMensagem(User user){
		topics = new ArrayList<MessageTopic>();
		this.user = user;
	}

	private boolean existTopic(String topic, MessageType type) {
		Iterator<MessageTopic> it = topics.iterator();
		if (user == null || topic == null || topic.isEmpty()) {
			// Vamo por mensagem dps
		}
		while (it.hasNext()) {
			MessageTopic nextTopic = it.next();
			if (nextTopic.getUser().equals(user)
					&& nextTopic.getSubject().equals(topic)) {
				return true;
			}
		}
		return false;
	}

	private boolean existTopic(String topic) {
		Iterator<MessageTopic> it = topics.iterator();
		if (topic == null || topic.isEmpty()) {
			// Vamo por mensagem dps
		}
		while (it.hasNext()) {
			MessageTopic nextTopic = it.next();
			if (nextTopic.getSubject().equals(topic)) {
				return true;
			}
		}
		return false;
	}

	public void addMessage2(Message message) {
		String topic = message.getSubject();
		if (!existTopic(topic, message.getType())) {
			topics.add(0, new MessageTopic(user, topic, message.getType()));
		}
		Iterator<MessageTopic> it = topics.iterator();
		while (it.hasNext()) {
			MessageTopic nextTopic = it.next();
			if (nextTopic.getUser().equals(user)
					&& nextTopic.getSubject().equals(topic)) {
				nextTopic.addMessage(message);
			}
		}
	}

	private void addMessage(Message message) {
		message.getSender().addMessage(message);
		message.getReceiver().addMessage(message);
	}

	/**
	 * Envia uma mensagem assim como as outras, entretanto o tipo padrao já eh
	 * de NEgociacao, e voce passa ql id do emprestimo...
	 * 
	 * @param sender
	 * @param receiver
	 * @param subject
	 * @param message
	 * @param idEmprestimo
	 * @return
	 * @throws Exception
	 */
	public String createMessage(User receiver, String subject,
			String message, String idEmprestimo) throws Exception {
		User sender = user;
		if (subject == null || subject.isEmpty()) {
			throw new InvalidSubject("Assunto inválido");
		}
		if (message == null || message.isEmpty()) {
			throw new InvalidMessage("Mensagem inválida");
		}
		try {
			sender.belongsLoan(idEmprestimo);
			receiver.belongsLoan(idEmprestimo);
		} catch( BorrowDoesNotExist e){
			if (UserManager.getUserManager().hasBorrowId(idEmprestimo)) throw new Exception("O usuário não participa deste empréstimo");
			throw new Exception(e.getMessage());
		}
		return createMessage(sender, receiver, subject, message,
				MessageType.NEGOCIACAO);
	}

	/**
	 * Cria uma mensagem
	 * 
	 * @param sender
	 *            Usuário que enviou a mensagem
	 * @param receiver
	 *            Usuário que recebeu a mensagem
	 * @param subject
	 *            Assunto da mensagem
	 * @param message
	 *            Corpo da mensagem
	 * @param messageType
	 *            Tipo da Mensagem
	 * @return
	 * @throws InvalidSender
	 * @throws InvalidReceiver
	 * @throws InvalidMessage
	 * @throws InvalidSubject
	 */
	public String createMessage(User receiver, String subject,
			String message, MessageType messageType) throws
			InvalidReceiver, InvalidMessage, InvalidSubject {
		User sender = user;
		if(messageType == null){
			throw new NullPointerException("aiui");
		}
		return createMessage(sender, receiver, subject, message, messageType);

	}

	/**
	 * Envia uma mensagem
	 * 
	 * @param sender
	 *            Remetetne
	 * @param receiver
	 *            Destinatario
	 * @param subject
	 *            Assunto
	 * @param message
	 *            Mensagem
	 * @param messageType
	 *            Se eh Off-Topic ou NEgociacao, use o enum MessageType
	 * @return Id do Topico que no caso eh o proprio subject
	 * @throws InvalidMessage
	 * @throws InvalidSubject
	 * @throws InvalidReceiver
	 */
	public String createMessage(User sender, User receiver, String subject,
			String message, MessageType messageType) throws InvalidMessage,
			InvalidSubject, InvalidReceiver {
		if (subject == null || subject.isEmpty()) {
			throw new InvalidSubject("Assunto inválido");
		}
		if (message == null || message.isEmpty()) {
			throw new InvalidMessage("Mensagem inválida");
		}
		addMessage(new Message(sender, receiver, subject, message, messageType));

		return subject;

	}

	/**
	 * Padrão Abstract, recebe uma string informando o Tipo e retorna o
	 * MessageType equivalente
	 * 
	 * @param tipo
	 * @return
	 * @throws InvalidType
	 * @throws InexistentType
	 */
	public MessageType converType(String tipo) throws InvalidType,
			InexistentType {
		if (tipo == null || tipo.isEmpty())
			throw new InvalidType("Tipo inválido");
		if (tipo.equals("offtopic"))
			return MessageType.OFF_TOPIC;
		if (tipo.equals("negociacao")) {
			return MessageType.NEGOCIACAO;
		}
		if (tipo.equals("todos"))
			return MessageType.GERAL;
		throw new InexistentType("Tipo inexistente");
	}

	/**
	 * Retorna uma lista de string contendo todos os tópicos do usuário que
	 * seguem determinado tipo de mensagem.
	 * 
	 * @param messageType
	 * @return
	 */
	public List<String> getAllTopics(MessageType messageType) {
		Iterator<MessageTopic> it = topics.iterator();
		List<String> ret = new ArrayList<String>();
		while (it.hasNext()) {
			MessageTopic next = it.next();
			if(messageType == MessageType.GERAL || messageType == next.getType()) {
				ret.add(next.getSubject());
			}
		}
		if (ret.size() == 0) {
			// Nao trata aqui
		}
		return ret;
	}

	/**
	 * Usa esse metodo no main, pq ele reotrna uma string
	 * 
	 * @param a
	 * @param messageType
	 * @return
	 */
	public String getTopics(MessageType messageType) {
		return convertListString(getAllTopics(messageType),
				"Não há tópicos criados");
	}
	
	public String getTopics(String messageType) throws InvalidType, InexistentType {
		return getTopics(converType(messageType));
	}

	/**
	 * Metodo auxiliar que recebe uma lista de string e converte em uma string é
	 * usado no teste de aceitação
	 * 
	 * @param ret
	 * @param emptyMessage
	 * @return
	 */
	public String convertListString(List<String> ret, String emptyMessage) {
		if (ret.size() == 0)
			return emptyMessage;
		Iterator<String> it = ret.iterator();
		String retValue = "";
		while (it.hasNext()) {
			String next = it.next();
			retValue =retValue.concat( next);
			if (it.hasNext())
				retValue =retValue.concat( "; ");
		}
		return retValue;
	}

	/**
	 * Converte uma lista de mensagens para uma list de string.
	 * 
	 * @param messages
	 * @return
	 */
	public List<String> convertMessagesToString(List<Message> messages) {
		List<String> ret = new ArrayList<String>();
		for (int i = 0; i < messages.size(); i++)
			ret.add(messages.get(i).getMessage());
		return ret;
	}

	/**
	 * Retorna uma string contendo todas as mensagens de um usuário de
	 * determinado tópico
	 * 
	 * @param user
	 * @param topic
	 * @return
	 * @throws InvalidTopic
	 * @throws InexistentTopic
	 */
	public String getStringMessages(String topic)
			throws InvalidTopic, InexistentTopic {
		return convertListString(convertMessagesToString(getAllMessagesByTopic(topic)), "Não há mensagens");
	}

	
	private boolean isValidTopic(String topic) {
		if (topic == null || topic.isEmpty())
			return false;
		return true;
	}

	/**
	 * Retorna uma lista de mensagens de um usuário de um determinado
	 * tópico
	 * @param user
	 * @param topic
	 * @return
	 * @throws InvalidTopic
	 * @throws InexistentTopic
	 */
	public List<Message> getAllMessagesByTopic(String topic)
			throws InvalidTopic, InexistentTopic {
		if (!isValidTopic(topic))
			throw new InvalidTopic("Identificador do tópico é inválido");

		if (!existTopic(topic, MessageType.GERAL)) {
			if (!existTopic(topic))
				throw new InexistentTopic("Tópico inexistente");
			else
				throw new InexistentTopic(
						"O usuário não tem permissão para ler as mensagens deste tópico");
		}
		Iterator<MessageTopic> it = topics.iterator();
		while (it.hasNext()) {
			MessageTopic next = it.next();
			if (next.getUser().equals(user) && next.getSubject().equals(topic)) {
				return next.getMessages();
			}
		}
		// NAO EXISTE ESSE TOPICO PARA ESSE USUARIO
		return new ArrayList<Message>();
	}
	
	@Override
	public void notifyEmprestimoAprovado(EmprestimoEvent event) {
		
	}

	@Override
	public void notifyTerminoDeEmprestimo(EmprestimoEvent event) {
		
	}

	@Override
	public void notifyRequisitarEmprestimo(EmprestimoEvent event) {
		User sender = event.getBeneficiado();
		User receiver = event.getDono();
		String subject = String.format("Empréstimo do item %s a %s", event.getNomeItem() ,event.getNomeBeneficiadoItem());
		String message = String.format("%s solicitou o empréstimo do item %s",event.getNomeBeneficiadoItem(), event.getNomeItem());

		try {
			sender.createMessage(receiver, subject, message, MessageType.NEGOCIACAO);
		} catch (Exception e) {}
		
	}

	@Override
	public void notifyRequisicaoDeDevolucao(EmprestimoEvent event) {
		User sender = event.getDono();
		User receiver = event.getBeneficiado();
		String subject = String.format("Empréstimo do item %s a %s", event.getNomeItem(), receiver.getName());
		String message = String.format("%s solicitou a devolução do item %s", sender.getName(), event.getNomeItem());
		
		try {
			sender.createMessage(receiver, subject, message, MessageType.NEGOCIACAO);
		} catch (Exception e) {}
		
	}

	@Override
	public void notifyOfereceuItem(ItemOferecidoEvent event) {
		try {
			event.getDonoItem().createMessage(event.getDonoPedido(), String.format("O usuário %s ofereceu o item %s", 
					event.getDonoItem().getName(),event.getItem().getName()), String.format("Item oferecido: %s - %s", 
							event.getItem().getName(),event.getItem().getDescription()), MessageType.OFF_TOPIC);
		} catch (Exception e) {}
		
	}

	@Override
	public void itemDevolvido(ItemEvent arg0) {
	}

	@Override
	public void notifyInteressePeloItem(InteressePeloItemEvent arg0) {
	}

	@Override
	public void notifyItemCadastrado(ItemEvent arg0) {
	}

	@Override
	public void notifyPublicarPedido(PublicarPedidoEvent arg0) {	
	}

}
